(defpackage :lem-vi-mode/tests/operator
  (:use :cl
        :lem
        :rove
        :lem-vi-mode/tests/utils)
  (:import-from :lem/common/killring
                :peek-killring-item)
  (:import-from :lem-fake-interface
                :with-fake-interface)
  (:import-from :named-readtables
                :in-readtable))
(in-package :lem-vi-mode/tests/operator)

(in-readtable :interpol-syntax)

(deftest vi-delete
  (with-fake-interface ()
    (with-vi-buffer (#?"a[b]c\ndef\nghi\njkl\n")
      (cmd "dd")
      (ok (buf= #?"d[e]f\nghi\njkl\n"))
      (cmd "dd")
      (ok (buf= #?"g[h]i\njkl\n"))
      (cmd "p")
      (ok (buf= #?"ghi\n[d]ef\njkl\n"))
      (cmd "2dd")
      (ok (buf= #?"ghi\n[]")))
    (with-vi-buffer (#?"abc\n[]")
      (cmd "dw")
      (ok (buf= #?"abc\n[]"))
      (cmd "dd")
      (ok (buf= #?"[a]bc")))
    (with-vi-buffer (#?"[a]bc\ndef\nghi\njkl\n")
      (cmd "1000dd")
      (ok (buf= "[]")))
    (testing "visual mode"
      (with-vi-buffer (#?"[a]bc\ndef\nghi\n")
        (cmd "Vjd")
        (ok (buf= #?"[g]hi\n"))))
    (testing "visual block mode"
      (with-vi-buffer (#?"ab[c]d\nefgh\n")
        (cmd "<C-v>hjd")
        (ok (buf= #?"a[d]\neh\n")))
      (with-vi-buffer (#?"a[b]cd\nefgh\n")
        (cmd "<C-v>jld")
        (ok (buf= #?"a[d]\neh\n")))
      (with-vi-buffer (#?"abcd\ne[f]gh\n")
        (cmd "<C-v>kld")
        (ok (buf= #?"a[d]\neh\n")))
      (with-vi-buffer (#?"abcd\nef[g]h\n")
        (cmd "<C-v>khd")
        (ok (buf= #?"a[d]\neh\n"))))
    (testing "with vi-forward-word-begin"
      (with-vi-buffer (#?"[a]bc\n  def\n")
        (cmd "dw")
        (ok (buf= #?"[\n]  def\n"))
        (cmd "dw")
        (ok (buf= #?"  [d]ef\n")))
      (with-vi-buffer (#?"abc\n[ ] def\n")
        (cmd "dw")
        (ok (buf= #?"abc\n[d]ef\n"))))
    (testing "daw"
      (with-vi-buffer ("foo b[a]r baz")
        (cmd "daw")
        (ok (buf= "foo [b]az")))
      (with-vi-buffer ("foo bar b[a]z")
        (cmd "daw")
        (ok (buf= "foo ba[r]")))
      (with-vi-buffer ("foo[ ] bar   baz")
        (cmd "daw")
        (ok (buf= "foo[ ]  baz")))
      (with-vi-buffer ("foo b[a]r baz")
        (cmd "2daw")
        (ok (buf= "fo[o]")))
      (with-vi-buffer ("foo b[a]r baz")
        (cmd "d2aw")
        (ok (buf= "fo[o]")))
      (with-vi-buffer ("foo b[a]r baz")
        (cmd "3daw")
        (ok (buf= "foo b[a]r baz")))
      (with-vi-buffer ("f[o]o$bar")
        (cmd "daw")
        (ok (buf= "[$]bar"))
        (cmd "daw")
        (ok (buf= "[b]ar")))
      (with-vi-buffer (#?"[]\n foo bar\n")
        (cmd "daw")
        (ok (buf= #?"[ ]bar\n"))))
    (testing "diw"
      (with-vi-buffer ("foo b[a]r baz")
        (cmd "diw")
        (ok (buf= "foo [ ]baz"))
        (cmd "diw")
        (ok (buf= "foo[b]az")))
      (with-vi-buffer ("foo b[a]r baz")
        (cmd "2diw")
        (ok (buf= "foo [b]az"))
        (cmd "2diw")
        (ok (buf= "foo [b]az")))
      (with-vi-buffer ("f[o]o$bar")
        (cmd "diw")
        (ok (buf= "[$]bar"))
        (cmd "diw")
        (ok (buf= "[b]ar")))
      (with-vi-buffer (#?"[]\n foo bar\n")
        (cmd "diw")
        (ok (buf= #?"[]\n foo bar\n"))))
    (testing "di\""
      (with-vi-buffer (" \"f[o]o\"  ")
        (cmd "di\"")
        (ok (buf= " \"[\"]  "))))
    (testing "dG"
      (with-vi-buffer (#?"abcd\ne[f]gh\nijkl\n")
        (cmd "dG")
        (ok (buf= #?"abcd\n[]"))))
    (testing "dc"
      (with-vi-buffer ("[a]bc")
        (ok (signals (cmd "dc") 'editor-abort))))
    (testing "d%"
      (with-vi-buffer (#?"abc[(]123)def\n")
        (cmd "d%")
        (ok (buf= #?"abc[d]ef\n")))
      (with-vi-buffer (#?"abc(123[)]def\n")
        (cmd "d%")
        (ok (buf= #?"abc[d]ef\n"))))))

(deftest vi-change
  (with-fake-interface ()
    (testing "change linewise"
      (with-vi-buffer (#?"a[b]c\ndef\n")
        (cmd "cc")
        (ok (buf= #?"[]\ndef\n")))
      (with-vi-buffer (#?"abc\nd[e]f")
        (cmd "cc")
        (ok (buf= #?"abc\n[]"))))
    (testing "change charwise"
      (with-vi-buffer (#?"a[b]c\ndef\n")
        (cmd "cl")
        (ok (buf= #?"a[]c\ndef\n"))))
    (with-vi-buffer ("")
      (ok (not (signals (cmd "cc")))))))

(deftest vi-change-whole-line
  (with-fake-interface ()
    (with-vi-buffer (#?"a[b]c\ndef\n")
      (cmd "S")
      (ok (buf= #?"[]\ndef\n")))
    (with-vi-buffer (#?"a[b]c\ndef\n")
      (cmd "2S")
      (ok (buf= #?"[]\n")))))

(deftest vi-join-line
  (with-fake-interface ()
    (lem:window-set-size (lem:current-window) 5 24)
    (with-vi-buffer (#?"[a]bcdefgh\nijklmn\n")
      (cmd "J")
      (ok (buf= #?"abcdefgh[ ]ijklmn\n")))))

(deftest vi-yank
  (with-fake-interface ()
    (flet ((last-kill ()
             (peek-killring-item (current-killring) 0)))
      (with-vi-buffer (#?"a[b]cd\n")
        (cmd "yl")
        (ok (buf= #?"a[b]cd\n"))
        (ok (equal (last-kill) "b")))
      (with-vi-buffer (#?"ef[g]h\n")
        (cmd "yh")
        (ok (buf= #?"e[f]gh\n"))
        (ok (equal (last-kill) "f")))
      (with-vi-buffer (#?"ab[c]d\nefgh\n")
        (cmd "yj")
        (ok (buf= #?"ab[c]d\nefgh\n"))
        (ok (equalp (multiple-value-list (last-kill))
                    (list #?"abcd\nefgh\n" '(:vi-line)))))
      (with-vi-buffer (#?"ijkl\n[m]nop\n")
        (cmd "yk")
        (ok (buf= #?"[i]jkl\nmnop\n"))
        (ok (equalp (multiple-value-list (last-kill))
                    (list #?"ijkl\nmnop\n" '(:vi-line)))))
      (with-vi-buffer (#?"ab[c]d\nefgh\n")
        (cmd "<C-v>hjy")
        (ok (buf= #?"a[b]cd\nefgh\n")))
      (with-vi-buffer (#?"a[b]cd\nefgh\n")
        (cmd "<C-v>jly")
        (ok (buf= #?"a[b]cd\nefgh\n")))
      (with-vi-buffer (#?"abcd\ne[f]gh\n")
        (cmd "<C-v>kly")
        (ok (buf= #?"a[b]cd\nefgh\n")))
      (with-vi-buffer (#?"abcd\nef[g]h\n")
        (cmd "<C-v>khy")
        (ok (buf= #?"a[b]cd\nefgh\n")))
      (with-vi-buffer (#?"abc[(]123)def\n")
        (cmd "y%")
        (ok (buf= #?"abc[(]123)def\n"))
        (ok (string= (last-kill) "(123)"))))))

(deftest vi-yank-line
  (with-fake-interface ()
    (with-vi-buffer (#?"a[b]cd\nefgh\n")
      (cmd "Y")
      (ok (buf= #?"a[b]cd\nefgh\n"))
      (cmd "jlp")
      (ok (buf= #?"abcd\nefgbc[d]h\n")))))

(deftest vi-delete-next-char
  (with-fake-interface ()
    (with-vi-buffer ("")
      (ok (not (signals (cmd "x")))))
    (with-vi-buffer (#?"[g]et-param\nget-option\n")
      (cmd "<C-v>f-jx")
      (ok (buf= #?"[p]aram\noption\n")))))

(deftest vi-delete-previous-char
  (with-fake-interface ()
    (with-vi-buffer (#?"[g]et-param\nget-option\n")
      (cmd "<C-v>f-jX")
      (ok (buf= #?"[p]aram\noption\n")))))

(deftest vi-substitute
  (with-fake-interface ()
    (with-vi-buffer (#?"[g]et-param\nget-option\n")
      (cmd "<C-v>f-js")
      (ok (buf= #?"[p]aram\noption\n")))))

(deftest vi-replace-char
  (with-fake-interface ()
    (with-vi-buffer ("a[n]t")
      (cmd "rr")
      (ok (buf= "a[r]t")))
    (with-vi-buffer ("sh[o]ut")
      (cmd "2re")
      (ok (buf= "she[e]t")))
    (with-vi-buffer ("<[m]>eat")
      (cmd "rb")
      (ok (buf= "[b]eat")))
    (with-vi-buffer ("p<i[n]>k")
      (cmd "re")
      (ok (buf= "p[e]ek")))
    (with-vi-buffer ("p<[i]c>k")
      (cmd "re")
      (ok (buf= "p[e]ek")))
    (with-vi-buffer (#?"em[a]cs\n")
      (cmd "VrX")
      (ok (buf= #?"[X]XXXX\n")))
    (with-vi-buffer (#?"a[b]cd\nefgh\n")
      (cmd "<C-v>jlrx")
      (ok (buf= #?"a[x]xd\nexxh\n")))
    (with-vi-buffer (#?"ab[c]d\nefgh\n")
      (cmd "<C-v>jhrx")
      (ok (buf= #?"a[x]xd\nexxh\n")))
    (with-vi-buffer (#?"abcd\nef[g]h\n")
      (cmd "<C-v>khrx")
      (ok (buf= #?"a[x]xd\nexxh\n")))
    (with-vi-buffer (#?"abcd\ne[f]gh\n")
      (cmd "<C-v>klrx")
      (ok (buf= #?"a[x]xd\nexxh\n")))))

(deftest vi-replace
  (with-fake-interface ()
    (with-vi-buffer ("a[n]t")
      (cmd "Rr<Esc>")
      (ok (buf= "a[r]t")))
    (with-vi-buffer ("sh[o]ut")
      (cmd "Rame<Esc>")
      (ok (buf= "sham[e]")))
    (with-vi-buffer ("[m]eat")
      (cmd "Rfruits<Esc>")
      (ok (buf= "fruit[s]")))))

(deftest vi-swapcase
  (with-fake-interface ()
    (with-vi-buffer ("[H]ello World")
      (cmd "g~l")
      (ok (buf= "[h]ello World")))
    (with-vi-buffer ("H[e]llo World")
      (cmd "g~l")
      (ok (buf= "H[E]llo World")))
    (with-vi-buffer ("[H]ello World")
      (cmd "2g~l")
      (ok (buf= "[h]Ello World")))
    (with-vi-buffer ("H[e]llo World")
      (cmd "2g~l")
      (ok (buf= "H[E]Llo World")))
    (with-vi-buffer ("Hell[o] World")
      (cmd "4g~l")
      (ok (buf= "Hell[O] wOrld")))
    (with-vi-buffer ("[H]ello World")
      (cmd "g~w")
      (ok (buf= "[h]ELLO World")))
    (with-vi-buffer ("[H]ello World")
      (cmd "g~e")
      (ok (buf= "[h]ELLO World")))
    (with-vi-buffer ("[H]ello World")
      (cmd "2g~w")
      (ok (buf= "[h]ELLO wORLD")))
    (with-vi-buffer ("[H]ello World")
      (cmd "g~2w")
      (ok (buf= "[h]ELLO wORLD")))
    (with-vi-buffer (#?"Hel[l]o\nWorld")
      (cmd "Vg~")
      (ok (buf= #?"[h]ELLO\nWorld")))
    (with-vi-buffer (#?"H[e]llo\nWorld")
      (cmd "<C-v>jlg~")
      (ok (buf= #?"H[E]Llo\nWORld")))
    (with-vi-buffer (#?"Hello\nWor[l]d")
      (cmd "<C-v>khg~")
      (ok (buf= #?"He[L]Lo\nWoRLd")))))

(deftest vi-swapcase-and-forward
  (with-fake-interface ()
    (with-vi-buffer ("[H]ello World")
      (cmd "~")
      (ok (buf= "h[e]llo World")))
    (with-vi-buffer ("[H]ello World")
      (cmd "~~~~~~")
      (ok (buf= "hELLO [W]orld")))))
